#pragma once 
#include <iostream>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#include "Util.hpp"
#include "Log.hpp"

using std::cout;
using std::endl;

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"

#define  OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code){//状态码和状态描述的映射
  std::string desc;
  switch(code){
    case 200:
      desc = "OK";
      break;
    case 404:
      desc = "Not Found";
      break;
    default:
      break;
  }
  return desc;
}

static std::string Suffix2Desc(const std::string& suffix){
  static std::unordered_map<std::string, std::string> suffix2desc = {
    {".html", "text/html"},
    {".css", "text/css"},
    {".js", "application/javascript"},
    {".jpg", "application/x-jpg"},
    {".xml", "application/xml"},
  };
  auto it = suffix2desc.find(suffix);
  if(it != suffix2desc.end()){
    return it->second;
  }
  return "text/html";
}
class HttpRequest{
  public:
    std::string request_line;//请求行
    std::vector<std::string> request_header;//请求报头 
    std::string blank;//空行
    std::string request_body;//正文

    //解析完毕之后的结果
    
    //请求行的解析结果
    std::string method;
    std::string uri;// path ? args
    std::string version;

    //请求包头的解析结果
    std::unordered_map<std::string, std::string> header_kv;
    int content_length;

    std::string path;
    std::string suffix;
    std::string query_string;

    bool cgi;
    int size;//服务端打开请求路径的文件fd且存在，该文件的大小
  public:
    HttpRequest():content_length(0),cgi(false)
      {} 
    ~HttpRequest()
      {}
};

class HttpResponse{
  public:
    std::string status_line;
    std::vector<std::string> response_header;
    std::string blank;
    std::string response_body;

    int status_code;
    int fd;//服务端打开请求路径的文件的fd
  public:
    HttpResponse():blank(LINE_END), status_code(OK), fd(-1)
      {}
    ~HttpResponse()
      {}
};

//读取请求，分析请求，构建响应
//IO通信
class EndPoint{
  private:
    int sock_;//服务端欢迎套接字接收的新到来的套接字，存在服务端
    HttpRequest http_request;
    HttpResponse http_response;
    bool stop;//标志读取过程是否出错

    bool RecvHttpRequestLine(){
      auto& line = http_request.request_line;
      if(Util::ReadLine(sock_, line) > 0){
        line.resize(line.size() - 1);
        LOG(INFO, http_request.request_line);
      }
      else{
        stop = false;
      }
      return stop;
    }
    bool RecvHttpRequestHeader(){ 
      std::string line;
      while(1){
        line.clear();
        if(Util::ReadLine(sock_, line) <= 0){
          stop = true;
          break;
        }
        if(line == "\n"){
          http_request.blank = line;
          break;
        }
        line.resize(line.size() - 1);
        http_request.request_header.push_back(line);
        LOG(INFO, line);
      }
      return stop;
    }
    void ParseHttpRequestLine(){
      auto& line = http_request.request_line;
      std::stringstream ss(line);

      ss >> http_request.method >> http_request.uri >> http_request.version;

      auto& method = http_request.method;
      std::transform(method.begin(), method.end(), method.begin(), ::toupper);//转换成为大写
    }
    void ParseHttpRequestHeader(){
      std::string key;
      std::string value;
      for(auto& e : http_request.request_header){
        if(Util::CutString(e , key, value, SEP)){
          http_request.header_kv[key] = value;
          //http_request.header_kv.insert(make_pair(key, value));
        }

      }
    }
    bool IsNeedRecvHttpRequestBody(){
      auto& method = http_request.method;
      if(method == "POST"){
        auto& header_kv = http_request.header_kv;
        auto it = header_kv.find("Content-Length");
        if(it != header_kv.end()){
          LOG(INFO, "Post Method, Content-Length: " + it->second);
          http_request.content_length = atoi(it->second.c_str());
          return true;
        }
      }
      return false;
    }
    bool RecvHttpRequestBody(){
      if(IsNeedRecvHttpRequestBody()){
        int content_length = http_request.content_length;
        auto& body = http_request.request_body;

        char ch = 0;
        while(content_length){
          ssize_t s = recv(sock_, &ch, 1, 0);
          if(s > 0){
            body.push_back(ch);
            content_length--;
          }
          else{
            stop = true;
            break;
          }
        }
        LOG(INFO, body);
      }
      return stop;
    } 
    int ProcessCgi(){//新线程，但是从头至尾就只有一个进程，就是httpserver
    
      int code = OK;
      //父进程的数据
      auto& method = http_request.method;
      auto& query_string = http_request.query_string; //GET
      auto& body_text = http_request.request_body;    //POST

      int content_length = http_request.content_length;
      auto& response_body = http_response.response_body;
      auto& bin = http_request.path;//要让子进程执行的目标程序，一定存在

      //设置进程级环境变量
      std::string query_string_env;//查询语句
      std::string method_env;//请求方法
      std::string content_length_env;//正文长度

      //站在父进程的角度
      int input[2];
      int output[2];

      if(pipe(input) < 0){
        LOG(ERROR, "pipe input error");
        code = SERVER_ERROR;
        return code;
      }
      if(pipe(output) < 0){
        LOG(ERROR, "pipe output error");
        code = SERVER_ERROR;
        return code;
      }


      pid_t pid = fork();
      if(pid == 0){//child，使用execl*函数进行程序替换
        close(output[1]);//子进程关闭output写端
        close(input[0]);//子进程关闭input读端


        method_env = "METHOD=";
        method_env += method;
        putenv((char*)method_env.c_str());

        cout << "debug child process is running! method: " << method << " method_env: " << method_env<< endl;
        cout << "bin: " << bin << endl;

        if(method == "GET"){//通过环境变量发送查询语句
          query_string_env = "QUERY_STRING=";
          query_string_env += query_string;
          putenv((char*)query_string_env.c_str());

          LOG(INFO, "Get Method, Add Query_String Env");
        }
        else if(method == "POST"){//通过匿名管道发送查询正文
          content_length_env = "CONTENT_LENGTH=";
          content_length_env += std::to_string(content_length);
          putenv((char*)content_length_env.c_str());

          cout << "Debug content_length_env: " << content_length_env << endl;
          LOG(INFO, "Post Method, Add Content_Length Env");
        }
        else{
          //do nothing
        }

        //重定向
        //完成管道读取~读取标准输入（fd = 0），管道写入~写入标准输出(fd = 1)
        //站在子进程角度
        //input[1]写端  --> 标准输出fd 1  -->input[1]
        //output[0]读端 --> 标准输入fd 0  -->output[0]
        dup2(input[1], 1);
        dup2(output[0], 0);


        //程序替换只替换代码和数据
        //并不会替换内核进程的数据，包括文件描述符表
        execl(bin.c_str(), bin.c_str(), nullptr);//使用程序替换执行cgi程序
        exit(1);
      }
      else if(pid < 0){//创建子进程error
        LOG(ERROR, "fork error");
        code = SERVER_ERROR;
        return code;
      }
      else{//父进程
        //父进程的写端 output[1]    父进程的读端  input[0]
        close(output[0]);//父进程关闭output读端 
        close(input[1]);//父进程关闭input写端

        if(method == "POST"){
          const char* start = body_text.c_str();
          int total = 0;
          int size = 0;
          while(total < content_length && (size = write(output[1], start+total, body_text.size()- total)) > 0){
            total += size;
          }
        }
        
        //接收cgi的处理结果
        char ch = 0;
        while(read(input[0], &ch, 1) > 0){
          response_body.push_back(ch);
        }

        int status = 0;
        pid_t ret = waitpid(pid, &status, 0);
        if(ret == pid){
          if(WIFEXITED(status)){
            if(WEXITSTATUS(status) == 0){
              code = OK;
            }
            else{
              code = BAD_REQUEST;
            }
          }
          else{
            code = SERVER_ERROR;
          }
        }

        close(output[1]);
        close(input[0]);
      }
      cout << "debug code: " << code << endl;
      return code;
    }
    int ProcessNonCgi(){
      http_response.fd = open(http_request.path.c_str(), O_RDONLY);
      if(http_response.fd >= 0){
         return OK;
      }
      return NOT_FOUND;
    }
    void BuildOkResponse(){
      std::string line = "Content-Type: ";
      line += Suffix2Desc(http_request.suffix);
      line += LINE_END;
      http_response.response_header.push_back(line);

      line = "Content-Length: ";
      if(http_request.cgi){
        line += std::to_string(http_response.response_body.size());//POST
      }
      else{
        line += std::to_string(http_request.size);//GET
      }
      line += LINE_END;
      http_response.response_header.push_back(line);
    }
    void HandlerError(std::string page){
      cout << "debug: " << page << endl;
      http_request.cgi = false;
      //要给用户返回对404页面
      http_response.fd = open(page.c_str(), O_RDONLY);
      if(http_response.fd > 0){
        struct stat st;
        stat(page.c_str(), &st);
        http_request.size = st.st_size;

        std::string line = "Content-Type: text/html";
        line += LINE_END;
        http_response. response_header.push_back(line);

        line = "Content-Length: ";
        line += std::to_string(st.st_size);
        line += LINE_END;
        http_response.response_header.push_back(line);
      }
    }
    //差错处理
    void BuildHttpResponseHelper(){
      auto& code = http_response.status_code;

      //构建差错响应行
      auto& status_line = http_response.status_line;
      status_line += HTTP_VERSION;
      status_line += " ";
      status_line += std::to_string(code);
      status_line += " ";
      status_line += Code2Desc(code);
      status_line += LINE_END;

      //构建响应正文，可能包括响应报头
      std::string path = WEB_ROOT;
      path += "/";
      switch(code){
        case OK:
          BuildOkResponse();
          break;
        case NOT_FOUND:
          path += PAGE_404;
          HandlerError(path);
          break;
        case BAD_REQUEST:
          path += PAGE_404;
          HandlerError(path);
        case SERVER_ERROR:
          path += PAGE_404;
          HandlerError(path);
        default:
          break;
      }
    }
  public:
    EndPoint(int sock):sock_(sock),stop(false)
      {}
    bool IsStop(){
      return stop;
    }
    void RecvHttpRequest(){
      //短路求值
      if((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader())){//读取请求行、请求报头
          ParseHttpRequestLine();//解析请求行
          ParseHttpRequestHeader();//解析请求报头
          RecvHttpRequestBody();//解析请求正文
      }

    }
    void BuildHttpResponse(){
      
      //请求已经全部读完
      std::string path_;
      auto& code = http_response.status_code;
      std::size_t found = 0;
      if(http_request.method != "GET" && http_request.method != "POST"){
        //非法请求
        LOG(WARNING, "method is not right");
        code = BAD_REQUEST;
        goto END;
      }
      if(http_request.method == "GET"){
        size_t pos = http_request.uri.find('?');
        if(pos != std::string::npos){
          Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
          //http_request.cgi = true;
        }
        else{
          http_request.path = http_request.uri;
        } 
      }
      else if(http_request.method == "POST"){
        //http_request.cgi = true;
        http_request.path = http_request.uri;
      }
      else{//Do Nothing
        
      }
      path_ = http_request.path;
      http_request.path = WEB_ROOT;
      http_request.path += path_;

      if(http_request.path[http_request.path.size() - 1] == '/'){
        http_request.path += HOME_PAGE;
      }
      
      struct stat st;//获取文件信息
      if(stat(http_request.path.c_str(), &st) == 0){//资源存在
        if(S_ISDIR(st.st_mode)){//说明请求的资源是一个目录，不被允许的
          //虽然是一个目录，但是绝对不会以/结尾
          http_request.path += "/";
          http_request.path += HOME_PAGE;
          stat(http_request.path.c_str(), &st);
        }
        if(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH){//目标文件是一个可执行文件
          //特殊处理
          http_request.cgi = true;
        }
         http_request.size = st.st_size;
      }
      else{//资源不存在
        LOG(WARNING,http_request.path + "Not Found!");
        code = NOT_FOUND;
        goto END;
      }

      found = http_request.path.rfind(".");
      if(found != std::string::npos){
        http_request.suffix = http_request.path.substr(found);
      }
      else{
        http_request.suffix = ".html";
      }

      if(http_request.cgi){//以CGI的方式处理
        code = ProcessCgi();
      }
      else{//以非CGI的方式处理，即简单的网页返回，返回静态网页
        //1、目标网页一定是存在的
        //2、返回并不是单单返回网页，而是要构建HTTP响应
        code = ProcessNonCgi();
      }
END:
      BuildHttpResponseHelper();
    }
    void SendHttpResponse(){
      send(sock_, http_response.status_line.c_str(), http_response.status_line.size(), 0);//发送响应行
      for(auto& it : http_response.response_header){//发送响应报头
        send(sock_, it.c_str(), it.size(), 0);
      }
      send(sock_, http_response.blank.c_str(), http_response.blank.size(), 0);//发送空行
      //fd, response_body 
      if(http_request.cgi){
        auto& response_body = http_response.response_body;
        cout << "debug " << "content-length" << http_response.response_body.size() << endl;
        cout << "-------------------------"<< endl;
        size_t size = 0;
        size_t total = 0;
        const char* start = response_body.c_str();
        while(total < response_body.size() && (size = send(sock_, start+total, response_body.size()-total, 0)) > 0){
          total += size;
        }
      }
      else{
        sendfile(sock_, http_response.fd, nullptr, http_request.size);//将服务器端读到的数据传输到欢迎套接字接收的新的套接字中
        close(http_response.fd);
      }
    }
    ~EndPoint(){
      close(sock_);
    }
};


//#define DEBUG 1
class CallBack{
  public:
    CallBack()
      {}
    void operator()(int sock){
      HandlerRequest(sock);
    }
    void HandlerRequest(int sock){
      LOG(INFO, "Handler Request begin!");

#ifdef DEBUG 
      char buffer[4096];
      recv(sock, buffer, sizeof(buffer), 0);
      cout << "----------------begin------------------" << endl;
      cout << buffer << endl;
      cout << "----------------end------------------" << endl;
#else
      EndPoint* ep = new EndPoint(sock);
      ep->RecvHttpRequest();
      if(!ep->IsStop()){//读取过程并没有出错
          LOG(INFO, "Recv No Error, Begin Build And Send");
          ep->BuildHttpResponse();
          ep->SendHttpResponse();
      }
      else{
        LOG(WARNING, "Recv Error, Stop Build And Send");
      }
      delete ep;
#endif 

      LOG(INFO, "Handler Request end!");
    }
    ~CallBack()
      {}
};
